﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Windows.Data;

namespace Fayshine.VA.Helpers
{
    public class PagedObservableCollectionView<T> : IPagedCollectionView, ICollectionView, INotifyCollectionChanged, IEditableCollectionView, INotifyPropertyChanged, IEnumerable<T>
    {
        PagedCollectionView m_PagedCollectionView;
        ObservableCollection<T> m_ObservableCollection;
        public PagedObservableCollectionView()
        {
            m_ObservableCollection = new ObservableCollection<T>();
            m_PagedCollectionView = new PagedCollectionView(m_ObservableCollection);
            m_PagedCollectionView.CollectionChanged += (s, e) =>
            {
                if (CollectionChanged != null) CollectionChanged(s, e);
            };
            m_PagedCollectionView.PageChanged += (s, e) =>
            {
                if (PageChanged != null) PageChanged(s, e);
            };
            m_PagedCollectionView.PageChanging += (s, e) =>
            {
                if (PageChanging != null) PageChanging(s, e);
            };
            m_PagedCollectionView.CurrentChanged += (s, e) =>
            {
                if (CurrentChanged != null) CurrentChanged(s, e);
            };
            m_PagedCollectionView.CurrentChanging += (s, e) =>
            {
                if (CurrentChanging != null) CurrentChanging(s, e);
            };
            m_PagedCollectionView.PropertyChanged += (s, e) =>
            {
                if (PropertyChanged != null) PropertyChanged(s, e);
            };

            Filter = IsMatch;
        }

        #region IPagedCollectionView Members

        public bool CanChangePage
        {
            get { return m_PagedCollectionView.CanChangePage; }
        }

        public bool IsPageChanging
        {
            get { return m_PagedCollectionView.IsPageChanging; }
        }

        public int ItemCount
        {
            get { return m_PagedCollectionView.ItemCount; }
        }

        public bool MoveToFirstPage()
        {
            return m_PagedCollectionView.MoveToFirstPage();
        }

        public bool MoveToLastPage()
        {
            return m_PagedCollectionView.MoveToLastPage();
        }

        public bool MoveToNextPage()
        {
            return m_PagedCollectionView.MoveToNextPage();
        }

        public bool MoveToPage(int pageIndex)
        {
            return m_PagedCollectionView.MoveToPage(pageIndex);
        }

        public bool MoveToPreviousPage()
        {
            return m_PagedCollectionView.MoveToPreviousPage();
        }

        public event EventHandler<EventArgs> PageChanged;

        public event EventHandler<PageChangingEventArgs> PageChanging;

        public int PageIndex
        {
            get
            {
                return m_PagedCollectionView.PageIndex;
            }
        }

        public int PageSize
        {
            get
            {
                return m_PagedCollectionView.PageSize;
            }
            set
            {
                m_PagedCollectionView.PageSize = value;
            }
        }

        public int TotalItemCount
        {
            get
            {
                return m_PagedCollectionView.TotalItemCount;
            }
        }

        #endregion

        #region ICollectionView Members

        public bool CanFilter
        {
            get { return m_PagedCollectionView.CanFilter; }
        }

        public bool CanGroup
        {
            get { return m_PagedCollectionView.CanGroup; }
        }

        public bool CanSort
        {
            get { return m_PagedCollectionView.CanSort; }
        }

        public bool Contains(object item)
        {
            return m_PagedCollectionView.Contains(item);
        }

        public System.Globalization.CultureInfo Culture
        {
            get
            {
                return m_PagedCollectionView.Culture;
            }
            set
            {
                m_PagedCollectionView.Culture = value;
            }
        }

        public event EventHandler CurrentChanged;

        public event CurrentChangingEventHandler CurrentChanging;

        public object CurrentItem
        {
            get { return m_PagedCollectionView.CurrentItem; }
        }

        public int CurrentPosition
        {
            get { return m_PagedCollectionView.CurrentPosition; }
        }

        public IDisposable DeferRefresh()
        {
            return m_PagedCollectionView.DeferRefresh();
        }

        public Predicate<object> Filter
        {
            get
            {
                return m_PagedCollectionView.Filter;
            }
            set
            {
                m_PagedCollectionView.Filter = value;
            }
        }

        public ObservableCollection<GroupDescription> GroupDescriptions
        {
            get { return m_PagedCollectionView.GroupDescriptions; }
        }

        public ReadOnlyObservableCollection<object> Groups
        {
            get { return m_PagedCollectionView.Groups; }
        }

        public bool IsCurrentAfterLast
        {
            get { return m_PagedCollectionView.IsCurrentAfterLast; }
        }

        public bool IsCurrentBeforeFirst
        {
            get { return m_PagedCollectionView.IsCurrentBeforeFirst; }
        }

        public bool IsEmpty
        {
            get { return m_PagedCollectionView.IsEmpty; }
        }

        public bool MoveCurrentTo(object item)
        {
            return m_PagedCollectionView.MoveCurrentTo(item);
        }

        public bool MoveCurrentToFirst()
        {
            return m_PagedCollectionView.MoveCurrentToFirst();
        }

        public bool MoveCurrentToLast()
        {
            return m_PagedCollectionView.MoveCurrentToLast();
        }

        public bool MoveCurrentToNext()
        {
            return m_PagedCollectionView.MoveCurrentToNext();
        }

        public bool MoveCurrentToPosition(int position)
        {
            return m_PagedCollectionView.MoveCurrentToPosition(position);
        }

        public bool MoveCurrentToPrevious()
        {
            return m_PagedCollectionView.MoveCurrentToPrevious();
        }

        public void Refresh()
        {
            m_PagedCollectionView.Refresh();
        }

        public SortDescriptionCollection SortDescriptions
        {
            get { return m_PagedCollectionView.SortDescriptions; }
        }

        public IEnumerable SourceCollection
        {
            get { return m_PagedCollectionView.SourceCollection; }
        }

        #endregion

        #region IEditableCollectionView Members

        public object AddNew()
        {
            return m_PagedCollectionView.AddNew();
        }

        public bool CanAddNew
        {
            get { return m_PagedCollectionView.CanAddNew; }
        }

        public bool CanCancelEdit
        {
            get { return m_PagedCollectionView.CanCancelEdit; }
        }

        public bool CanRemove
        {
            get { return m_PagedCollectionView.CanRemove; }
        }

        public void CancelEdit()
        {
            m_PagedCollectionView.CancelEdit();
        }

        public void CancelNew()
        {
            m_PagedCollectionView.CancelNew();
        }

        public void CommitEdit()
        {
            m_PagedCollectionView.CommitEdit();
        }

        public void CommitNew()
        {
            m_PagedCollectionView.CommitNew();
        }

        public object CurrentAddItem
        {
            get { return m_PagedCollectionView.CurrentAddItem; }
        }

        public object CurrentEditItem
        {
            get { return m_PagedCollectionView.CurrentEditItem; }
        }

        public void EditItem(object item)
        {
            m_PagedCollectionView.EditItem(item);
        }

        public bool IsAddingNew
        {
            get
            {
                return m_PagedCollectionView.IsAddingNew;
            }
        }

        public bool IsEditingItem
        {
            get
            {
                return m_PagedCollectionView.IsEditingItem;
            }
        }

        public NewItemPlaceholderPosition NewItemPlaceholderPosition
        {
            get
            {
                return m_PagedCollectionView.NewItemPlaceholderPosition;
            }
            set
            {
                m_PagedCollectionView.NewItemPlaceholderPosition = value;
            }
        }

        public void Remove(object item)
        {
            m_ObservableCollection.Remove((T)item);
        }

        public void RemoveAt(int index)
        {
            m_PagedCollectionView.RemoveAt(index);
        }

        public void Clear()
        {
            m_ObservableCollection.Clear();
        }

        public int IndexOf(object item)
        {
            return m_PagedCollectionView.IndexOf(item);
        }

        #endregion

        #region INotifyCollectionChanged Members

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            foreach (var t in m_PagedCollectionView)
            {
                yield return (T)t;
            }
        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion

        public IEnumerable<T> GetAllMatched()
        {
            return this.m_ObservableCollection.Where(t => IsMatch(t));
        }
        public IEnumerable<T> GetAll()
        {
            return this.m_ObservableCollection;
        }
        private FilterDescription m_FilterDescription;
        public FilterDescription FilterDescription
        {
            get { return m_FilterDescription; }
            set
            {
                if (m_FilterDescription != value)
                {
                    m_FilterDescription = value;
                    OnPropertyChanged("FilterDescription");
                    m_Filter = null;
                    this.Refresh();
                }
            }
        }
        private string m_FilterExpression;
        public string FilterExpression
        {
            get { return m_FilterExpression; }
            internal set
            {
                if (m_FilterExpression != value)
                {
                    m_FilterExpression = value;
                    OnPropertyChanged("FilterExpression");
                }
            }
        }


        Func<T, bool> m_Filter;

        bool IsMatch(object obj)
        {
            if (m_Filter == null)
            {
                m_Filter = GenerateFilter();
            }
            bool result = (bool)m_Filter((T)obj);
            return result;
        }
        Func<T, bool> GenerateFilter()
        {
            if (FilterDescription == null) return t => true;
            ParameterExpression param = Expression.Variable(typeof(T), "it");
            Expression compare = FilterDescription.ToExpression<T>(param);
            if (compare == null) return t => true;
            Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(compare, param);
            FilterExpression = lambda.ToString();

            return lambda.Compile();
        }

        public void Add(T t)
        {
            m_ObservableCollection.Add(t);
        }

        #region INotifyPropertyChanged Members

        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion

    }
}
